package com.gao.spring.annotation;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * @Projectname SSM
 * @Filename LoggerAspect
 * @Author an
 * @Data 2022/8/2 16:40
 * @Description TODO
 * 1、在切面中，需要通过指定的注解将方法标识为通知方法
 * @Before：前置通知，在目标对象方法执行之前执行
 * @After：后置通知，在目标方法的finally中
 */


/*
*2.切入点表达式，设置在标识通知的注解的value属性中
* "execution(public int com.gao.spring.annotation.CalculatorImpl.add(int,int))"
* "execution(* com.gao.spring.annotation.CalculatorImpl.*(..))"
*第一个 * 表示任意的访问修饰符和返回值类型
*第二个 * 表示类中的任意方法
* .. 表示任意的类的参数列表
* 类的地方也可以使用*，表示包下所有的类execution(* com.gao.spring.annotation.*.*(..))
*
* 3.重用切入点表达式 @Pointcut
*  @Pointcut("execution(* com.gao.spring.annotation.CalculatorImpl.*(..))")
    public void pointCut(){}
* */

    /*
    * 4.获取连接点信息
    * 在通知方法的参数位置，设置JoinPoint类型的参数，就可以获取连接点所对应方法的信息
    *获取连接点所对应的签名信息  Signature signature = joinPoint.getSignature();
    *获取连接点所对应的方法的参数  String args = Arrays.toString(joinPoint.getArgs());
    * */

@Component
@Aspect  //将当前组件标识为切面
// @Aspect表示这个类是一个切面类
// @Component注解保证这个切面类能够放入IOC容器
public class LoggerAspect {

    @Pointcut("execution(* com.gao.spring.annotation.CalculatorImpl.*(..))")
    public void pointCut(){}

//    @Before("execution(public int com.gao.spring.annotation.CalculatorImpl.add(int,int))")
//    @Before("execution(* com.gao.spring.annotation.CalculatorImpl.*(..))")
    @Before("pointCut()")
    public void beforeAdviceMethod(JoinPoint joinPoint){
        //获取连接点所对应的签名信息
        Signature signature = joinPoint.getSignature();
        //获取连接点所对应的方法的参数
        String args = Arrays.toString(joinPoint.getArgs());
        System.out.println("LoggerAspect-->前置通知,方法名："+signature.getName()+"，参数："+args);
    }

    @After("pointCut()")
    public void afterAdviceMethod(JoinPoint joinPoint){
        //获取连接点所对应的签名信息
        Signature signature = joinPoint.getSignature();
        String args = Arrays.toString(joinPoint.getArgs());
        //获取连接点所对应的方法的参数
        System.out.println("LoggerAspect-->后置通知,方法名："+signature.getName()+",参数:"+args);
    }


    /*
    * 在返回通知中若要获取目标对象方法的返回值
    * 只需要通过@AfterReturning注解的returning属性
    * 就可以将通知方法的某个参数指定为接收目标对象方法的返回值参数
    * */
    @AfterReturning(value = "pointCut()",returning = "result")
    public void afterReturningAdviceMethod(JoinPoint joinPoint,Object result){
        //获取连接点所对应的签名信息
        Signature signature = joinPoint.getSignature();
        //获取连接点所对应的方法的参数
        String args = Arrays.toString(joinPoint.getArgs());
        System.out.println("LoggerAspect-->返回通知，方法名："+signature.getName()+"结果："+result);
    }

    /*
     * 在异常通知中若要获取目标对象方法的返回值
     * 只需要通过@AfterThrowing注解的throwing属性
     * 就可以将通知方法的某个参数指定为接收目标对象方法出现的异常的参数
     * */
    @AfterThrowing(value = "pointCut()",throwing ="exception" )
    public void afterThrowingAdviceMethod(JoinPoint joinPoint,Throwable exception){
        //获取连接点所对应的签名信息
        Signature signature = joinPoint.getSignature();
        //获取连接点所对应的方法的参数
        System.out.println("LoggerAspect-->后置通知,方法名："+signature.getName()+"异常："+exception);
    }

    @Around("pointCut()")
    //环绕通知的方法的返回值一定要和目标对象的返回值一致
    public Object arroundAdviceMethod(ProceedingJoinPoint joinPoint){
        Object result = null;
        try {
            System.out.println("环绕通知的前置通知");
            //表示目标对象方法的执行
            result = joinPoint.proceed();
            System.out.println("环绕通知的返回通知");
        } catch (Throwable e) {
            e.printStackTrace();
            System.out.println("环绕通知的异常通知");
        }finally {
            System.out.println("环绕通知的后置通知");
        }
        return result;


    }
}
